#pragma once

#include "compat.h"

#ifdef __cplusplus
#   include <boost/numeric/conversion/cast.hpp>
#   define NUMERIC_CAST(type, value) (boost::numeric_cast<type>(value))
#else
#   define NUMERIC_CAST(type, value) ((type)(value))
#endif

#ifdef __cplusplus
    // C++ allows you to omit parameter names if they're unused
#   define OMIT_PARAM
#else
    // A variable name must be provided in C, so make one up and mark it unused
#   define OMIT_PARAM3(uniq) const omitted_parameter_##uniq VARATTR_UNUSED
#   define OMIT_PARAM2(uniq) OMIT_PARAM3(uniq)
#   define OMIT_PARAM OMIT_PARAM2(__COUNTER__)
#endif

#define UNUSED_MEMBER2(uniq) unused##uniq
#define UNUSED_MEMBER UNUSED_MEMBER2(__COUNTER__)

// get the byte size of an element of arr
#ifdef __cplusplus
	extern "C++" {
		template <typename T, size_t size>
		size_t COUNTOF(T(&)[size]) {
			return size;
		}
	}
#else
	// intentionally not using parentheses for element size
	// this may help catch non-array usage
	#define COUNTOF(arr) (sizeof(arr) / sizeof(arr[0]))
#endif

#ifdef NDEBUG
#   define verify(expr) ((void)(expr))
#else
#   define verify(expr) assert(expr)
#endif

#define expect(expr) \
    ((expr) \
    ? (void)(0) \
    : error_at_line(0, errno, __FILE__, __LINE__, "False: `%s'", #expr))

#define expect_eq(exp, act, fmt) do { \
        typeof(exp) _exp = (exp); \
        typeof(act) _act = (act); \
        ((_exp == _act) \
        ? (void)(0) \
        : error_at_line(0, errno, __FILE__, __LINE__, \
                fmt " (%s) != " fmt " (%s)", _exp, #exp, _act, #act)); \
    } while (0)

//    _Pragma("GCC diagnostic push")
//    _Pragma("GCC diagnostic ignored \"-Wshadow\"")
//    _Pragma("GCC diagnostic pop")

#if defined(__cplusplus) && !defined(__GNUC__)
#   include <algorithm>
#   define MIN std::min
#   define MAX std::max
//#   define TMIN(T, a, b) std::min<T>(a, b)
//#   define TMAX(T, a, b) std::max<T>(a, b)
#else
#	define _CHOOSE2(binoper, lexpr, lvar, rexpr, rvar) \
		({ \
			decltype(lexpr) lvar = (lexpr); \
			decltype(rexpr) rvar = (rexpr); \
			lvar binoper rvar ? lvar : rvar; \
		})
#	define _CHOOSE_VAR2(prefix, unique) prefix##unique
#	define _CHOOSE_VAR(prefix, unique) _CHOOSE_VAR2(prefix, unique)
#	define _CHOOSE(binoper, lexpr, rexpr) \
		_CHOOSE2( \
			binoper, \
			lexpr, _CHOOSE_VAR(_left, __COUNTER__), \
			rexpr, _CHOOSE_VAR(_right, __COUNTER__) \
		)
#	define MIN(a, b) _CHOOSE(<, a, b)
#	define MAX(a, b) _CHOOSE(>, a, b)
#endif

#ifndef NDEBUG
    static inline bool util_mem_is_zeroed(void const *const p_, size_t n)
    {
        char const *p = void_cast(char const *, p_);
        while (n--)
        {
            if (*p++)
                return false;
        }
        return true;
    }
#endif
